﻿#if WINDOWS_PHONE
#region File and License Information
/*
<File>
	<License>
		Copyright © 2009 - 2017, Daniel Vaughan. All rights reserved.
		This file is part of Calcium (http://calciumsdk.net), 
		which is released under the MIT License.
		See file /Documentation/License.txt for details.
	</License>
	<CreationDate>2011-11-20 17:42:11Z</CreationDate>
</File>
*/
#endregion

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Navigation;

using Outcoder.ComponentModel;
using Microsoft.Phone.Controls;

namespace Outcoder.NavigationModel
{
	public class NavigationMonitor
	{
		public void Initialize()
		{
			if (!(Application.Current.RootVisual is PhoneApplicationFrame))
			{
				/* This is invoked in case this call is made before the RootVisual has been assigned. */
				Deployment.Current.Dispatcher.BeginInvoke(InitializeCore);
			}
			else
			{
				InitializeCore();
			}
		}

		void InitializeCore()
		{
			try
			{
				var frame = (PhoneApplicationFrame)Application.Current.RootVisual;
				frame.Navigating -= HandleNavigating;
				frame.Navigating += HandleNavigating;
				frame.Navigated -= HandleNavigated;
				frame.Navigated += HandleNavigated;
				frame.BackKeyPress -= HandleBackKeyPress;
				frame.BackKeyPress += HandleBackKeyPress;
			}
			catch (Exception ex)
			{
				throw new Exception("NavigationMonitor Initialize method raised exception.", ex);
			}
		}

		void HandleBackKeyPress(object sender, CancelEventArgs e)
		{
			ProcessDataContext(e,
				(navigationAware, eventArgs, parent) =>
				{
					if (eventArgs.Cancel)
					{
						return;
					}

					var frame = (PhoneApplicationFrame)Application.Current.RootVisual;

					Uri source = new Uri("app://external/", UriKind.Absolute);
					IEnumerator<JournalEntry> backStackEnumerator = frame.BackStack.GetEnumerator();
					while (backStackEnumerator.MoveNext())
					{
						JournalEntry journalEntry = backStackEnumerator.Current;
						source = journalEntry.Source;
					}

					NavigatingCancelEventArgs args = new NavigatingCancelEventArgs(source, NavigationMode.Back, true, false);
					navigationAware.HandleNavigatingFrom(new NavigatingCancelArgs(args));
					if (args.Cancel)
					{
						e.Cancel = true;
					}
				});
		}

		void HandleNavigating(object sender, NavigatingCancelEventArgs e)
		{
			if (e.NavigationMode == NavigationMode.Back)
			{
				return;
			}

			ProcessDataContext(e,
				(navigationAware, eventArgs, parent) =>
				{
					if (eventArgs.Cancel)
					{
						return;
					}
					navigationAware.HandleNavigatingFrom(new NavigatingCancelArgs(eventArgs));
					if (eventArgs.Cancel && parent != null)
					{
						parent.ActivateViewModel((ViewModelBase)navigationAware);
					}
				});
		}

		void HandleNavigated(object sender, NavigationEventArgs e)
		{
			var frame = (Frame)Application.Current.RootVisual;
			if (!e.Uri.Equals(frame.CurrentSource))
			{
				return;
			}
			ProcessDataContext(e,
				(navigationAware, eventArgs, _) => navigationAware.HandleNavigatedTo(new NavigationArgs(eventArgs)));
		}

		void ProcessDataContext<TEventArgs>(TEventArgs e,
			Action<INavigationAware, TEventArgs, ICompositeViewModel> notifyAction)
			where TEventArgs : EventArgs
		{
			object dataContext = GetContentDataContext();
			if (dataContext == null)
			{
				return;
			}

			ProcessDataContext(dataContext, e, notifyAction, null);
		}

		static void ProcessDataContext<TEventArgs>(
			object dataContext, TEventArgs e, Action<INavigationAware, TEventArgs, ICompositeViewModel> notifyAction,
			ICompositeViewModel parent)
			where TEventArgs : EventArgs
		{
			INavigationAware navigationAware = dataContext as INavigationAware;
			if (navigationAware != null)
			{
				notifyAction(navigationAware, e, parent);
			}

			/* Process child view models recursively. */
			var compositeViewModel = dataContext as ICompositeViewModel;
			if (compositeViewModel != null && compositeViewModel.ChildViewModels != null)
			{
				foreach (var childViewModel in compositeViewModel.ChildViewModels)
				{
					ProcessDataContext(childViewModel, e, notifyAction, compositeViewModel);
				}
			}
		}

		object GetContentDataContext()
		{
			var frame = (Frame)Application.Current.RootVisual;
			FrameworkElement element = frame.Content as FrameworkElement;
			if (element != null)
			{
				return element.DataContext;
			}
			return null;
		}
	}
}
#endif